/*
	Copyright (c) 2007, ANYwebcam.com Pty Ltd. All rights reserved.

	The software in this package is published under the terms of the BSD style 
	license, a copy of which has been included with this distribution in the 
	license.txt file.
*/
package com.anywebcam.di.component
{
	import com.anywebcam.di.*;
	import com.anywebcam.di.error.*;
	import com.anywebcam.reflection.*;

	import mx.utils.ObjectUtil;

	/**
	 * Instantiates an instance of the managed type and injects its dependencies into its constructor
	 */
	public class ConstructorInjectionComponent extends InstantiatingComponent
	{
		/**
		 * @inheritDoc
		 */
		public function ConstructorInjectionComponent( key:Object, type:Class, parameters:Array=null )
		{
			super( key, type, parameters );
		}
		
		/**
 		 * @inheritDoc
		 */
		override public function getInstance( container:DIContainer ):Object
		{
			return createInstance( getConstructorParameters( container ) );
		}
		
		/**
		 * Get the parameters for the constructor from the supplied container
		 */
		protected function getConstructorParameters( container:DIContainer ):Array
		{
			// fixme: provide an option for resolving all parameters
			// fixme: provide an option for resolving just the required parameters
			// fixme: provide an option for resolving only that parameters that were supplied to the component's constructor
			return classInfo.constructor.parameters.map( 
				function( type:Class, index:int, array:Array ):Object 
				{ 
					return getConstructorParameterValue( container, type, index, array );
				});
		}
		
		/**
		 * Given a Class get an instance of that type from the DIContainer using whatever hinting is available in the components parameters
		 */
		protected function getConstructorParameterValue( container:DIContainer, type:Class, index:int, array:Array ):Object
		{
			//trace( this, 'getConstructorParameterValue', type, index, ObjectUtil.toString( parameters ) );
			
			if( parameters && parameters[ index ] )
			{
				var param:Object = parameters[ index ];			
				
				if( !(param is String) )
				{
					for( var key:String in param )
					{
						param = param[ key ];
						break;
					}
				}
				
				return getParameterValue( container, param );
			}
			
			return container.getInstance( type );
		}
		
		/**
		 * Create an instance of the type using the supplied parameters
		 */
		protected function createInstance( params:Array=null ):Object
		{
			var instance:Object;
			
			// use only defined params not constructor.length() due to optional parameters
			// yay for actionscript 3 not supporting Function.apply() for Class constructors
			switch( params.length )
			{
				case 0: 
					instance = new type();
					break;	
				case 1: 
					instance = new type( params[0] );
					break;
				case 2: 
					instance = new type( params[0], params[1] );
					break;
				case 3:
					instance = new type( params[0], params[1], params[2] );
					break;
				case 4:
					instance = new type( params[0], params[1], params[2], params[3] );
					break;
				case 5:
					instance = new type( params[0], params[1], params[2], params[3], params[4] );
					break;
				case 6:
					instance = new type( params[0], params[1], params[2], params[3], params[4], params[5] );
					break;
				case 7:
					instance = new type( params[0], params[1], params[2], params[3], params[4], params[5], params[6] );
					break;
				case 8:
					instance = new type( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7] );
					break;
				case 9:
					instance = new type( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8] );
					break;
				case 10:
					instance = new type( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9] );
					break;
				default:
					throw new DIComponentError( this, 'Unable to instantiate '+ type +' , more then 10 constructor parameters, extend ConstructorInjectionComponent::createInstance' );
					break;
			}
		
			return instance;
		}
	}
}